    local imgui = require 'imgui'
    local vmajor, vminor, vpatch = string.match(imgui._VERSION, '(%d+)%.(%d+)%.(%d+)')
    assert(tonumber(vmajor) == 1 and (tonumber(vminor) > 1 or (tonumber(vminor) == 1 and tonumber(vpatch) >= 3)),
        'ImGui Pie requires Moon ImGui v1.1.4 or greater')
    local ImVec2 = imgui.ImVec2
    local ImVec4 = imgui.ImVec4
    local ImColor = imgui.ImColor
    
    local function ImRectAdd(rect, rhs)
        local Min, Max = rect.Min, rect.Max
        if Min.x > rhs.x then Min.x = rhs.x end
        if Min.y > rhs.y then Min.y = rhs.y end
        if Max.x < rhs.x then Max.x = rhs.x end
        if Max.y < rhs.y then Max.y = rhs.y end
    end
    
    local function NewPieMenu(context)
        local obj = {
            m_iCurrentIndex = 0,
            m_fMaxItemSqrDiameter = 0,
            m_fLastMaxItemSqrDiameter = 0,
            m_iHoveredItem = 0,
            m_iLastHoveredItem = 0,
            m_iClickedItem = 0,
            m_oItemIsSubMenu = {},
            m_oItemNames = {},
            m_oItemSizes = {},
        }
        return obj
    end
    
    local function NewPieMenuContext(MaxPieMenuStack, MaxPieItemCount, RadiusEmpty, RadiusMin, MinItemCount, MinItemCountPerLevel)
        local obj = {
            c_iMaxPieMenuStack = MaxPieMenuStack or 8,
            c_iMaxPieItemCount = MaxPieItemCount or 12,
            c_iRadiusEmpty = RadiusEmpty or 30,
            c_iRadiusMin = RadiusMin or 30,
            c_iMinItemCount = MinItemCount or 3,
            c_iMinItemCountPerLevel = MinItemCountPerLevel or 3,
    
            m_oPieMenuStack = {},
            m_iCurrentIndex = -1,
            m_iLastFrame = 0,
            m_iMaxIndex = 0,
            m_oCenter = ImVec2(0, 0),
            m_iMouseButton = 0,
            m_bClose = false,
        }
        for i = 0, obj.c_iMaxPieMenuStack - 1 do
            obj.m_oPieMenuStack[i] = NewPieMenu(obj)
        end
        return obj
    end
    
    local function BeginPieMenuEx(menuCtx)
        assert(menuCtx.m_iCurrentIndex < menuCtx.c_iMaxPieMenuStack)
        menuCtx.m_iCurrentIndex = menuCtx.m_iCurrentIndex + 1
        menuCtx.m_iMaxIndex = menuCtx.m_iMaxIndex + 1
        local oPieMenu = menuCtx.m_oPieMenuStack[menuCtx.m_iCurrentIndex]
        oPieMenu.m_iCurrentIndex = 0
        oPieMenu.m_fMaxItemSqrDiameter = 0
        if not imgui.IsMouseReleased( menuCtx.m_iMouseButton ) then
            oPieMenu.m_iHoveredItem = -1
        end
        if menuCtx.m_iCurrentIndex > 0 then
            oPieMenu.m_fMaxItemSqrDiameter = menuCtx.m_oPieMenuStack[menuCtx.m_iCurrentIndex - 1].m_fMaxItemSqrDiameter
        end
    end
    
    local function EndPieMenuEx(menuCtx)
        assert(menuCtx.m_iCurrentIndex >= 0)
        local oPieMenu = menuCtx.m_oPieMenuStack[menuCtx.m_iCurrentIndex]
        menuCtx.m_iCurrentIndex = menuCtx.m_iCurrentIndex - 1
    end
    
    local function BeginPiePopup(menuCtx, pName, iMouseButton)
        iMouseButton = iMouseButton or 0
        if imgui.IsPopupOpen(pName) then
            imgui.PushStyleColor(imgui.Col.WindowBg, ImVec4(0, 0, 0, 0))
            imgui.PushStyleColor(imgui.Col.Border, ImVec4(0, 0, 0, 0))
            imgui.PushStyleVar(imgui.StyleVar.WindowRounding, 0.0)
            imgui.PushStyleVar(imgui.StyleVar.Alpha, 1.0)
            menuCtx.m_iMouseButton = iMouseButton
            menuCtx.m_bClose = false
            imgui.SetNextWindowPos( ImVec2( -100, -100 ), imgui.Cond.Appearing )
            imgui.SetNextWindowSize(ImVec2(0, 0), imgui.Cond.Always)
            local bOpened = imgui.BeginPopup(pName)
            if bOpened then
                local iCurrentFrame = imgui.GetFrameCount()
                if menuCtx.m_iLastFrame < (iCurrentFrame - 1) then
                    menuCtx.m_oCenter = ImVec2(imgui.GetIO().MousePos)
                end
                menuCtx.m_iLastFrame = iCurrentFrame
                menuCtx.m_iMaxIndex = -1
                BeginPieMenuEx(menuCtx)
                return true
            else
                imgui.End()
                imgui.PopStyleColor(2)
                imgui.PopStyleVar(2)
            end
        end
        return false
    end
    
    local function EndPiePopup(menuCtx)
        EndPieMenuEx(menuCtx)
        local oStyle = imgui.GetStyle()
        local pDrawList = imgui.GetWindowDrawList()
        pDrawList:PushClipRectFullScreen()
        local oMousePos = imgui.GetIO().MousePos
        local oDragDelta = ImVec2(oMousePos.x - menuCtx.m_oCenter.x, oMousePos.y - menuCtx.m_oCenter.y)
        local fDragDistSqr = oDragDelta.x*oDragDelta.x + oDragDelta.y*oDragDelta.y
        local fCurrentRadius = menuCtx.c_iRadiusEmpty
        local oArea = {Min = ImVec2(menuCtx.m_oCenter), Max = ImVec2(menuCtx.m_oCenter)}
        local bItemHovered = false
        local c_fDefaultRotate = -math.pi / 2
        local fLastRotate = c_fDefaultRotate
        for iIndex = 0, menuCtx.m_iMaxIndex do
            local oPieMenu = menuCtx.m_oPieMenuStack[iIndex]
            local fMenuHeight = math.sqrt(oPieMenu.m_fMaxItemSqrDiameter)
            local fMinRadius = fCurrentRadius
            local fMaxRadius = fMinRadius + (fMenuHeight * oPieMenu.m_iCurrentIndex) / 2
            local item_arc_span = 2 * math.pi / math.max(menuCtx.c_iMinItemCount + menuCtx.c_iMinItemCountPerLevel * iIndex, oPieMenu.m_iCurrentIndex)
            local drag_angle = math.atan2(oDragDelta.y, oDragDelta.x)
            local fRotate = fLastRotate - item_arc_span * ( oPieMenu.m_iCurrentIndex - 1 ) / 2
            local item_hovered = -1
            for item_n = 0, oPieMenu.m_iCurrentIndex - 1 do
                local item_label = oPieMenu.m_oItemNames[ item_n ]
                local inner_spacing = oStyle.ItemInnerSpacing.x / fMinRadius / 2
                local fMinInnerSpacing = oStyle.ItemInnerSpacing.x / ( fMinRadius * 2 )
                local fMaxInnerSpacing = oStyle.ItemInnerSpacing.x / ( fMaxRadius * 2 )
                local item_inner_ang_min = item_arc_span * ( item_n - 0.5 + fMinInnerSpacing ) + fRotate
                local item_inner_ang_max = item_arc_span * ( item_n + 0.5 - fMinInnerSpacing ) + fRotate
                local item_outer_ang_min = item_arc_span * ( item_n - 0.5 + fMaxInnerSpacing ) + fRotate
                local item_outer_ang_max = item_arc_span * ( item_n + 0.5 - fMaxInnerSpacing ) + fRotate
                local hovered = false
                if fDragDistSqr >= fMinRadius * fMinRadius and fDragDistSqr < fMaxRadius * fMaxRadius  then
                    while (drag_angle - item_inner_ang_min) < 0 do
                        drag_angle = drag_angle + (2 * math.pi)
                    end
                    while (drag_angle - item_inner_ang_min) > 2 * math.pi do
                        drag_angle = drag_angle - (2 * math.pi)
                    end
                    if drag_angle >= item_inner_ang_min and drag_angle < item_inner_ang_max  then
                        hovered = true
                        bItemHovered = not oPieMenu.m_oItemIsSubMenu[ item_n ]
                    end
                end
                local arc_segments = math.floor(( 32 * item_arc_span / ( 2 * math.pi ) ) + 1)
                local iColor = imgui.GetStyleColorU32( hovered and imgui.Col.ButtonHovered or imgui.Col.Button )
                local fAngleStepInner = (item_inner_ang_max - item_inner_ang_min) / arc_segments
                local fAngleStepOuter = ( item_outer_ang_max - item_outer_ang_min ) / arc_segments
                pDrawList:PrimReserve(arc_segments * 6, (arc_segments + 1) * 2)
                for iSeg = 0, arc_segments do
                    local fCosInner = math.cos(item_inner_ang_min + fAngleStepInner * iSeg)
                    local fSinInner = math.sin(item_inner_ang_min + fAngleStepInner * iSeg)
                    local fCosOuter = math.cos(item_outer_ang_min + fAngleStepOuter * iSeg)
                    local fSinOuter = math.sin(item_outer_ang_min + fAngleStepOuter * iSeg)
    
                    if iSeg < arc_segments then
                        local VtxCurrentIdx = pDrawList._VtxCurrentIdx
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 0)
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 2)
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 1)
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 3)
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 2)
                        pDrawList:PrimWriteIdx(VtxCurrentIdx + 1)
                    end
                    local pos = ImVec2(menuCtx.m_oCenter.x + fCosInner * (fMinRadius + oStyle.ItemInnerSpacing.x), menuCtx.m_oCenter.y + fSinInner * (fMinRadius + oStyle.ItemInnerSpacing.x))
                    local pos2 = ImVec2(menuCtx.m_oCenter.x + fCosOuter * (fMaxRadius - oStyle.ItemInnerSpacing.x), menuCtx.m_oCenter.y + fSinOuter * (fMaxRadius - oStyle.ItemInnerSpacing.x))
                    pDrawList:PrimWriteVtx(pos, ImVec2(0, 0), iColor)
                    pDrawList:PrimWriteVtx(pos2, ImVec2(0, 0), iColor)
                end
    
                local fRadCenter = ( item_arc_span * item_n ) + fRotate
                local oOuterCenter = ImVec2( menuCtx.m_oCenter.x + math.cos( fRadCenter ) * fMaxRadius, menuCtx.m_oCenter.y + math.sin( fRadCenter ) * fMaxRadius )
                ImRectAdd(oArea, oOuterCenter)
                if oPieMenu.m_oItemIsSubMenu[item_n] then
                    local oTrianglePos = {ImVec2(), ImVec2(), ImVec2()}
                    local fRadLeft = fRadCenter - 5 / fMaxRadius
                    local fRadRight = fRadCenter + 5 / fMaxRadius
                    oTrianglePos[ 0+1 ].x = menuCtx.m_oCenter.x + math.cos( fRadCenter ) * ( fMaxRadius - 5 )
                    oTrianglePos[ 0+1 ].y = menuCtx.m_oCenter.y + math.sin( fRadCenter ) * ( fMaxRadius - 5 )
                    oTrianglePos[ 1+1 ].x = menuCtx.m_oCenter.x + math.cos( fRadLeft ) * ( fMaxRadius - 10 )
                    oTrianglePos[ 1+1 ].y = menuCtx.m_oCenter.y + math.sin( fRadLeft ) * ( fMaxRadius - 10 )
                    oTrianglePos[ 2+1 ].x = menuCtx.m_oCenter.x + math.cos( fRadRight ) * ( fMaxRadius - 10 )
                    oTrianglePos[ 2+1 ].y = menuCtx.m_oCenter.y + math.sin( fRadRight ) * ( fMaxRadius - 10 )
                    pDrawList:AddTriangleFilled(oTrianglePos[1], oTrianglePos[2], oTrianglePos[3], 0xFFFFFFFF)
                end
                local text_size = ImVec2(oPieMenu.m_oItemSizes[item_n])
                local text_pos = ImVec2(
                    menuCtx.m_oCenter.x + math.cos((item_inner_ang_min + item_inner_ang_max) * 0.5) * (fMinRadius + fMaxRadius) * 0.5 - text_size.x * 0.5,
                    menuCtx.m_oCenter.y + math.sin((item_inner_ang_min + item_inner_ang_max) * 0.5) * (fMinRadius + fMaxRadius) * 0.5 - text_size.y * 0.5)
                pDrawList:AddText(text_pos, imgui.GetStyleColorU32(imgui.Col.Text), item_label)
                if hovered then
                    item_hovered = item_n
                end
            end
            fCurrentRadius = fMaxRadius
            oPieMenu.m_fLastMaxItemSqrDiameter = oPieMenu.m_fMaxItemSqrDiameter
            oPieMenu.m_iHoveredItem = item_hovered
            if fDragDistSqr >= fMaxRadius * fMaxRadius then
                item_hovered = oPieMenu.m_iLastHoveredItem
            end
            oPieMenu.m_iLastHoveredItem = item_hovered
            fLastRotate = item_arc_span * oPieMenu.m_iLastHoveredItem + fRotate
            if item_hovered == -1 or not oPieMenu.m_oItemIsSubMenu[item_hovered] then
                break
            end
        end
        pDrawList:PopClipRect()
        if oArea.Min.x < 0  then
            menuCtx.m_oCenter.x = ( menuCtx.m_oCenter.x - oArea.Min.x )
        end
        if oArea.Min.y < 0  then
            menuCtx.m_oCenter.y = ( menuCtx.m_oCenter.y - oArea.Min.y )
        end
        local oDisplaySize = imgui.GetIO().DisplaySize
        if oArea.Max.x > oDisplaySize.x  then
            menuCtx.m_oCenter.x = ( menuCtx.m_oCenter.x - oArea.Max.x ) + oDisplaySize.x
        end
        if oArea.Max.y > oDisplaySize.y  then
            menuCtx.m_oCenter.y = ( menuCtx.m_oCenter.y - oArea.Max.y ) + oDisplaySize.y
        end
        if menuCtx.m_bClose or ( not bItemHovered and imgui.IsMouseReleased( menuCtx.m_iMouseButton ) ) then
            imgui.CloseCurrentPopup()
        end
        imgui.EndPopup()
        imgui.PopStyleColor(2)
        imgui.PopStyleVar(2)
    end
    
    local function BeginPieMenu(menuCtx, pName, bEnabled)
        assert(menuCtx.m_iCurrentIndex >= 0 and menuCtx.m_iCurrentIndex < menuCtx.c_iMaxPieItemCount)
        bEnabled = bEnabled or true
        local oPieMenu = menuCtx.m_oPieMenuStack[menuCtx.m_iCurrentIndex]
        local oTextSize = imgui.CalcTextSize(pName, true)
        oPieMenu.m_oItemSizes[oPieMenu.m_iCurrentIndex] = oTextSize
         local fSqrDiameter = oTextSize.x * oTextSize.x + oTextSize.y * oTextSize.y
        if fSqrDiameter > oPieMenu.m_fMaxItemSqrDiameter then
            oPieMenu.m_fMaxItemSqrDiameter = fSqrDiameter
        end
        oPieMenu.m_oItemIsSubMenu[oPieMenu.m_iCurrentIndex] = true
        oPieMenu.m_oItemNames[oPieMenu.m_iCurrentIndex] = pName
        if oPieMenu.m_iLastHoveredItem == oPieMenu.m_iCurrentIndex then
            oPieMenu.m_iCurrentIndex = oPieMenu.m_iCurrentIndex + 1
            BeginPieMenuEx(menuCtx)
            return true
        end
        oPieMenu.m_iCurrentIndex = oPieMenu.m_iCurrentIndex + 1
        return false
    end
    
    local function EndPieMenu(menuCtx)
        assert(menuCtx.m_iCurrentIndex >= 0 and menuCtx.m_iCurrentIndex < menuCtx.c_iMaxPieItemCount)
        menuCtx.m_iCurrentIndex = menuCtx.m_iCurrentIndex - 1
    end
    
    local function PieMenuItem(menuCtx, pName, bEnabled)
        assert(menuCtx.m_iCurrentIndex >= 0 and menuCtx.m_iCurrentIndex < menuCtx.c_iMaxPieItemCount)
        bEnabled = bEnabled or true
        local oPieMenu = menuCtx.m_oPieMenuStack[menuCtx.m_iCurrentIndex]
        local oTextSize = imgui.CalcTextSize(pName, true)
        oPieMenu.m_oItemSizes[oPieMenu.m_iCurrentIndex] = oTextSize
        local fSqrDiameter = oTextSize.x * oTextSize.x + oTextSize.y * oTextSize.y
        if fSqrDiameter > oPieMenu.m_fMaxItemSqrDiameter then
            oPieMenu.m_fMaxItemSqrDiameter = fSqrDiameter
        end
        oPieMenu.m_oItemIsSubMenu[oPieMenu.m_iCurrentIndex] = false
        oPieMenu.m_oItemNames[oPieMenu.m_iCurrentIndex] = pName
        local bActive = oPieMenu.m_iCurrentIndex == oPieMenu.m_iHoveredItem
        oPieMenu.m_iCurrentIndex = oPieMenu.m_iCurrentIndex + 1
        if bActive then
            menuCtx.m_bClose = true
        end
        return bActive
    end
    
    local function New(...)
        local menuContext = NewPieMenuContext(...)
        return {
            BeginPiePopup = function(name, mouseButton)
                return BeginPiePopup(menuContext, name, mouseButton)
            end,
            EndPiePopup = function()
                return EndPiePopup(menuContext)
            end,
            PieMenuItem = function(name, enabled)
                return PieMenuItem(menuContext, name, enabled)
            end,
            BeginPieMenu = function(name, enabled)
                return BeginPieMenu(menuContext, name, enabled)
            end,
            EndPieMenu = function()
                return EndPieMenu(menuContext)
            end
        }
    end
    
    local defaultPieMenu = New()
    defaultPieMenu.New = New
    return defaultPieMenu
